home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / ufw / common.py < prev    next >
Encoding:
Python Source  |  2008-10-08  |  10.7 KB  |  342 lines

  1. #
  2. # common.py: common classes for ufw
  3. #
  4. # Copyright (C) 2008 Canonical Ltd.
  5. #
  6. #    This program is free software: you can redistribute it and/or modify
  7. #    it under the terms of the GNU General Public License version 3,
  8. #    as published by the Free Software Foundation.
  9. #
  10. #    This program is distributed in the hope that it will be useful,
  11. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #    GNU General Public License for more details.
  14. #
  15. #    You should have received a copy of the GNU General Public License
  16. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. #
  18.  
  19. import re
  20. import socket
  21. import ufw.util
  22. from ufw.util import debug
  23.  
  24. programName = "ufw"
  25. state_dir = "/var/lib/ufw"
  26. config_dir = "/etc"
  27.  
  28. class UFWError(Exception):
  29.     '''This class represents ufw exceptions'''
  30.     def __init__(self, value):
  31.         self.value = value
  32.  
  33.     def __str__(self):
  34.         return repr(self.value)
  35.  
  36.  
  37. class UFWRule:
  38.     '''This class represents firewall rules'''
  39.     def __init__(self, action, protocol, dport="any", dst="0.0.0.0/0",
  40.                  sport="any", src="0.0.0.0/0"):
  41.         # Be sure to update dup_rule accordingly...
  42.         self.remove = False
  43.         self.updated = False
  44.         self.v6 = False
  45.         self.dst = ""
  46.         self.src = ""
  47.         self.dport = ""
  48.         self.sport = ""
  49.         self.protocol = ""
  50.         self.multi = False
  51.         self.dapp = ""
  52.         self.sapp = ""
  53.         self.action = ""
  54.         try:
  55.             self.set_action(action)
  56.             self.set_protocol(protocol)
  57.             self.set_port(dport)
  58.             self.set_port(sport, "src")
  59.             self.set_src(src)
  60.             self.set_dst(dst)
  61.         except UFWError:
  62.             raise
  63.  
  64.     def __str__(self):
  65.         return self.format_rule()
  66.  
  67.     def dup_rule(self):
  68.         '''Return a duplicate of a rule'''
  69.         rule = UFWRule(self.action, self.protocol)
  70.         rule.dport = self.dport
  71.         rule.sport = self.sport
  72.         rule.dst = self.dst
  73.         rule.src = self.src
  74.         rule.remove = self.remove
  75.         rule.updated = self.updated
  76.         rule.v6 = self.v6
  77.         rule.multi = self.multi
  78.         rule.dapp = self.dapp
  79.         rule.sapp = self.sapp
  80.  
  81.         return rule
  82.  
  83.     def format_rule(self):
  84.         '''Format rule for for later parsing'''
  85.         str = ""
  86.  
  87.         # Protocol is handled below
  88.         if self.protocol == "any":
  89.             str = " -p all"
  90.         else:
  91.             str = " -p " + self.protocol
  92.  
  93.             if self.multi:
  94.                 str += " -m multiport"
  95.                 if self.dport != "any" and self.sport != "any":
  96.                     str += " --dports " + self.dport
  97.                     str += " -m multiport"
  98.                     str += " --sports " + self.sport
  99.                 elif self.dport != "any":
  100.                     str += " --dports " + self.dport
  101.                 elif self.sport != "any":
  102.                     str += " --sports " + self.sport
  103.  
  104.         if self.dst != "0.0.0.0/0" and self.dst != "::/0":
  105.             str += " -d " + self.dst
  106.         if not self.multi and self.dport != "any":
  107.             str += " --dport " + self.dport
  108.         if self.src != "0.0.0.0/0" and self.src != "::/0":
  109.             str += " -s " + self.src
  110.         if not self.multi and self.sport != "any":
  111.             str += " --sport " + self.sport
  112.         if self.action == "allow":
  113.             str += " -j ACCEPT"
  114.         elif self.action == "limit":
  115.             # Caller needs to change this
  116.             str += " -j LIMIT"
  117.         else:
  118.             str += " -j DROP"
  119.  
  120.         if self.dapp != "" or self.sapp != "":
  121.             # Format the comment string, and quote it just in case
  122.             comment = "-m comment --comment '"
  123.             pat_space = re.compile(' ')
  124.             if self.dapp != "":
  125.                 comment += "dapp_" + pat_space.sub('%20', self.dapp)
  126.             if self.dapp != "" and self.sapp != "":
  127.                 comment += ","
  128.             if self.sapp != "":
  129.                 comment += "sapp_" + pat_space.sub('%20', self.sapp)
  130.             comment += "'"
  131.  
  132.             str += " " + comment
  133.  
  134.         return str.strip()
  135.  
  136.     def set_action(self, action):
  137.         '''Sets action of the rule'''
  138.         if action.lower() == "allow":
  139.             self.action = action
  140.         elif action.lower() == "limit":
  141.             self.action = "limit"
  142.         else:
  143.             self.action = "deny"
  144.  
  145.     def set_port(self, port, loc="dst"):
  146.         '''Sets port and location (destination or source) of the rule'''
  147.         err_msg = _("Bad port '%s'") % (port)
  148.         if port == "any":
  149.             pass
  150.         elif loc == "dst" and self.dapp:
  151.             pass
  152.         elif loc == "src" and self.sapp:
  153.             pass
  154.         elif re.match(r'^[,:]', port) or re.match(r'[,:]$', port):
  155.             raise UFWError(err_msg)
  156.         elif (port.count(',') + port.count(':')) > 14:
  157.             # Limitation of iptables
  158.             raise UFWError(err_msg)
  159.         else:
  160.             ports = port.split(',')
  161.             if len(ports) < 1:
  162.                 raise UFWError(err_msg)
  163.             elif len(ports) > 1:
  164.                 self.multi = True
  165.  
  166.             tmp = ""
  167.             for p in ports:
  168.                 if re.match(r'^\d+:\d+$', p):
  169.                     # Port range
  170.                     self.multi = True
  171.                     ran = p.split(':')
  172.                     if len(ran) != 2:
  173.                         raise UFWError(err_msg)
  174.                     for q in ran:
  175.                         if int(q) < 1 or int(q) > 65535:
  176.                             raise UFWError(err_msg)
  177.                     if int(ran[0]) >= int(ran[1]):
  178.                         raise UFWError(err_msg)
  179.                 elif re.match('^\d+$', p):
  180.                     if int(p) < 1 or int(p) > 65535:
  181.                         raise UFWError(err_msg)
  182.                 elif re.match(r'^\w[\w\-]+', p):
  183.                     try:
  184.                         p = socket.getservbyname(p)
  185.                     except Exception, (error):
  186.                         raise UFWError(err_msg)
  187.                 else:
  188.                     raise UFWError(err_msg)
  189.  
  190.                 if tmp:
  191.                     tmp += "," + str(p)
  192.                 else:
  193.                     tmp = str(p)
  194.  
  195.             port = tmp
  196.  
  197.         if loc == "src":
  198.             self.sport = str(port)
  199.         else:
  200.             self.dport = str(port)
  201.  
  202.     def set_protocol(self, protocol):
  203.         '''Sets protocol of the rule'''
  204.         if protocol == "tcp" or protocol == "udp" or protocol == "any":
  205.             self.protocol = protocol
  206.         else:
  207.             err_msg = _("Unsupported protocol '%s'") % (protocol)
  208.             raise UFWError(err_msg)
  209.  
  210.     def _fix_anywhere(self):
  211.         '''Adjusts src and dst based on v6'''
  212.         if self.v6:
  213.             if self.dst and (self.dst == "any" or self.dst == "0.0.0.0/0"):
  214.                 self.dst = "::/0"
  215.             if self.src and (self.src == "any" or self.src == "0.0.0.0/0"):
  216.                 self.src = "::/0"
  217.         else:
  218.             if self.dst and (self.dst == "any" or self.dst == "::/0"):
  219.                 self.dst = "0.0.0.0/0"
  220.             if self.src and (self.src == "any" or self.src == "::/0"):
  221.                 self.src = "0.0.0.0/0"
  222.  
  223.     def set_v6(self, v6):
  224.         '''Sets whether this is ipv6 rule, and adjusts src and dst 
  225.            accordingly.
  226.         '''
  227.         self.v6 = v6
  228.         self._fix_anywhere()
  229.  
  230.     def set_src(self, addr):
  231.         '''Sets source address of rule'''
  232.         tmp = addr.lower()
  233.  
  234.         if tmp != "any" and not ufw.util.valid_address(tmp, "any"):
  235.             err_msg = _("Bad source address")
  236.             raise UFWError(err_msg)
  237.         self.src = tmp
  238.         self._fix_anywhere()
  239.  
  240.     def set_dst(self, addr):
  241.         '''Sets destination address of rule'''
  242.         tmp = addr.lower()
  243.  
  244.         if tmp != "any" and not ufw.util.valid_address(tmp, "any"):
  245.             err_msg = _("Bad destination address")
  246.             raise UFWError(err_msg)
  247.         self.dst = tmp
  248.         self._fix_anywhere()
  249.  
  250.     def normalize(self):
  251.         '''Normalize src and dst to standard form'''
  252.         changed = False
  253.         if self.src:
  254.             try:
  255.                 (self.src, changed) = ufw.util.normalize_address(self.src, \
  256.                                                                  self.v6)
  257.             except Exception:
  258.                 raise
  259.                 err_msg = _("Could not normalize source address")
  260.                 raise UFWError(err_msg)
  261.         if changed:
  262.             self.updated = changed
  263.  
  264.         if self.dst:
  265.             try:
  266.                 (self.dst, changed) = ufw.util.normalize_address(self.dst, \
  267.                                                                    self.v6)
  268.             except Exception:
  269.                 err_msg = _("Could not normalize destination address")
  270.                 raise UFWError(err_msg)
  271.  
  272.         if self.dport:
  273.             ports = self.dport.split(',')
  274.             ufw.util.human_sort(ports)
  275.             self.dport = ','.join(ports)
  276.  
  277.         if self.sport:
  278.             ports = self.sport.split(',')
  279.             ufw.util.human_sort(ports)
  280.             self.sport = ','.join(ports)
  281.  
  282.         if changed:
  283.             self.updated = changed
  284.  
  285.     def match(x, y):
  286.         '''Check if rules match
  287.         Return codes:
  288.           0  match
  289.           1  no match
  290.          -1  match all but action
  291.         '''
  292.         if not x or not y:
  293.             raise ValueError()
  294.  
  295.         dbg_msg = _("No match")
  296.         if x.dport != y.dport:
  297.             debug(dbg_msg)
  298.             return 1
  299.         if x.sport != y.sport:
  300.             debug(dbg_msg)
  301.             return 1
  302.         if x.protocol != y.protocol:
  303.             debug(dbg_msg)
  304.             return 1
  305.         if x.src != y.src:
  306.             debug(dbg_msg)
  307.             return 1
  308.         if x.dst != y.dst:
  309.             debug(dbg_msg)
  310.             return 1
  311.         if x.v6 != y.v6:
  312.             debug(dbg_msg)
  313.             return 1
  314.         if x.dapp != y.dapp:
  315.             debug(dbg_msg)
  316.             return 1
  317.         if x.sapp != y.sapp:
  318.             debug(dbg_msg)
  319.             return 1
  320.         if x.action == y.action:
  321.             dbg_msg = _("Found exact match")
  322.             debug(dbg_msg)
  323.             return 0
  324.         dbg_msg = _("Found non-action match")
  325.         debug(dbg_msg)
  326.         return -1
  327.  
  328.     def get_app_tuple(self):
  329.         '''Returns a tuple to identify an app rule'''
  330.         tuple = ""
  331.         if self.dapp != "" or self.sapp != "":
  332.             tuple = "%s %s %s %s" % (self.dapp, self.dst, self.sapp, self.src)
  333.             if self.dapp == "":
  334.                 tuple = "%s %s %s %s" % (self.dport, self.dst, self.sapp, \
  335.                                          self.src)
  336.             if self.sapp == "":
  337.                 tuple = "%s %s %s %s" % (self.dapp, self.dst, self.sport, \
  338.                                          self.src)
  339.  
  340.         return tuple
  341.  
  342.